#include "stdafx.h"
#include "MouseTracerManagerImpl5.h"
#include "MouseTracer.h"
#include "MGCfg.h"
#include "SSMutex.h"

CMouseTracerManagerImpl5::CMouseProcHook::CMouseProcHook() : m_hHook(NULL), m_lParam(0)
{
	AUTO_MUTEX(CMouseTracerManagerImpl5);
}

CMouseTracerManagerImpl5::CMouseProcHook::~CMouseProcHook()
{
	AUTO_MUTEX(CMouseTracerManagerImpl5);

	if(m_hHook)
	{
		::UnhookWindowsHookEx(m_hHook);
	}
}


BOOL CMouseTracerManagerImpl5::CMouseProcHook::SetMouseProcHook(HINSTANCE hmod, DWORD dwThreadID)
{
	AUTO_MUTEX(CMouseTracerManagerImpl5);

	m_thunk.Init(CMouseTracerManagerImpl5::MouseProc, this);
	m_hHook = ::SetWindowsHookEx(WH_MOUSE, (HOOKPROC)&(m_thunk.thunk), hmod, dwThreadID);
	if(NULL == m_hHook)
		MessageBox(NULL, _T("Failed to set up the Mouse Hook"), _T("DragIt Error"), MB_OK | MB_ICONERROR);
	TRACE(_T("\n\n::SetWindowsHookEx(), HINSTANCE=(%x),TID=(%x),&thunk=(%x),HHOOK=(%x)\n\n\n"), hmod, dwThreadID, &(m_thunk.thunk), m_hHook);

	return (NULL != m_hHook);
}

void CMouseTracerManagerImpl5::CMouseProcHook::UnhookMouseProcHook()
{
	AUTO_MUTEX(CMouseTracerManagerImpl5);

	if(m_hHook)
	{
		TRACE(_T("\n\n::UnhookWindowsHookEx(), TID=(%x), HHOOK=(%x)\n\n\n"), ::GetCurrentThreadId(), m_hHook);
		::UnhookWindowsHookEx(m_hHook);
		m_hHook = NULL;
	}
}
			
CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl5> > *CMouseTracerManagerImpl5::m_pTracer = NULL;
CMouseTracerManagerImpl5::CMouseProcHookMap CMouseTracerManagerImpl5::m_mouseProcHookMap;
BOOL CMouseTracerManagerImpl5::m_bTracerDeleted = TRUE;

CMouseTracerManagerImpl5::CMouseTracerManagerImpl5()
{
	AUTO_MUTEX(CMouseTracerManagerImpl5);
}

CMouseTracerManagerImpl5::~CMouseTracerManagerImpl5()
{
	AUTO_MUTEX(CMouseTracerManagerImpl5);

	std::for_each<CMouseTracerManagerImpl5::CMouseProcHookMap::iterator>(
		m_mouseProcHookMap.begin(),
		m_mouseProcHookMap.end(),
		CMouseTracerManagerImpl5::CMouseProcHookMap::DeleteAndNull() );

	m_mouseProcHookMap.clear();

	// m_pTracer will be automatically deleted on receiving WM_NCDESTROY
}

/**
 *	CMouseTracerManagerImpl5::Setup(HINSTANCE hMod)
 *
 *
 *	@return BOOL
 *
 */

BOOL CMouseTracerManagerImpl5::Setup(HINSTANCE hMod/* = NULL*/, DWORD dwThreadID/* = 0*/)
{
	AUTO_MUTEX(CMouseTracerManagerImpl5);

	if(0 == dwThreadID)
		dwThreadID = ::GetCurrentThreadId();
	CMouseTracerManagerImpl5::CMouseProcHookMap::iterator itMap = m_mouseProcHookMap.find(dwThreadID);
	if(m_mouseProcHookMap.end() != itMap)
		return TRUE;

	CMouseTracerManagerImpl5::CMouseProcHook *pMouseProcHook = new CMouseTracerManagerImpl5::CMouseProcHook;
	BOOL bReturn = pMouseProcHook->SetMouseProcHook(hMod, dwThreadID);
	if(bReturn)
		m_mouseProcHookMap[dwThreadID] = (LPVOID)pMouseProcHook;
	
	return bReturn;
}

/**
 *	CMouseTracerManagerImpl5::Remove()
 *
 *
 *	@return void
 *
 */

void CMouseTracerManagerImpl5::Remove(DWORD dwThreadID/* = 0*/)
{
	AUTO_MUTEX(CMouseTracerManagerImpl5);
	if(0 == dwThreadID)
		dwThreadID = ::GetCurrentThreadId();
	CMouseTracerManagerImpl5::CMouseProcHookMap::iterator itMap = m_mouseProcHookMap.find(dwThreadID);
	if(m_mouseProcHookMap.end() != itMap)
	{
		if(itMap->second)
			((CMouseProcHook *)itMap->second)->UnhookMouseProcHook();
		delete itMap->second;
		m_mouseProcHookMap.erase(itMap);
	}

	if (m_pTracer && !CMouseTracerManagerImpl5::m_bTracerDeleted)
	{
		if (::IsWindow(m_pTracer->GetHwnd()))
		{
			m_pTracer->Unhook();
			m_pTracer->Delete();
			m_pTracer = NULL;
		}
	}
}

typedef CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl5> > MouseTracer;

/**
 *	CMouseTracerManagerImpl5::MouseProc()
 *
 *	@param int nCode
 *	@param WPARAM wParam
 *	@param LPARAM lParam
 *
 *	@return LRESULT CALLBACK
 *
 */

LRESULT CALLBACK CMouseTracerManagerImpl5::MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	CMouseProcHook *pThis = reinterpret_cast<CMouseProcHook *>(lParam);
	lParam = pThis->GetLPARAM();
	
	if(nCode < 0)
		return ::CallNextHookEx(pThis->GetHHOOK(), nCode, wParam, lParam);
	
    if (nCode == HC_ACTION)
    {
        if (lParam)
        {
			
#if(_WIN32_WINNT >= 0x0500)
			
            MOUSEHOOKSTRUCTEX *pMHEX = reinterpret_cast<MOUSEHOOKSTRUCTEX *>(lParam);
			MOUSEHOOKSTRUCT *pMH = (MOUSEHOOKSTRUCT *)pMHEX;
			
#else	// #if(_WIN32_WINNT >= 0x0500)
			
            MOUSEHOOKSTRUCT *pMH = reinterpret_cast<MOUSEHOOKSTRUCT *>(lParam);
			
#endif	// #if(_WIN32_WINNT >= 0x0500)
			
			int nCatTarget;
			switch (wParam)
			{
			case WM_RBUTTONDOWN:
			case WM_NCRBUTTONDOWN:
				if (MGCfg::Get().Option().GetMGAButton() == MG::BUTTON_RIGHT)
				{
					AUTO_MUTEX(CMouseTracerManagerImpl5);
					
					if ((m_pTracer && !m_pTracer->IsTemporarySuspended()) || !m_pTracer)
					{
						if (m_pTracer && MGCfg::Get().Option().IsShowRecognitionOnStatusBar() && MGCfg::Get().Option().IsIdentifyTargetWindowClass())
							MGCfg::Get().Action().IdentifyTargetWindowClass(pMH->hwnd);
						
						if (!MGCfg::Get().Action().IsMGInAction())
						{
							if(-1 != (nCatTarget = MGCfg::Get().Action().CheckTargetValid(pMH->hwnd)))
							{
								if (m_pTracer && !CMouseTracerManagerImpl5::m_bTracerDeleted)
								{
									if (m_pTracer->GetHwnd() != pMH->hwnd)
									{
										// check to see if the hooked window have already been destroyed
										if (::IsWindow(m_pTracer->GetHwnd()))
										{
											m_pTracer->Unhook();
											m_pTracer->Delete();
											m_pTracer = NULL;
										}
									}
									else 
										break;
								}
								m_pTracer = (MouseTracer *)new MouseTracer(TRUE, &CMouseTracerManagerImpl5::m_bTracerDeleted);
								m_pTracer->Hook(pMH->hwnd);
								m_pTracer->SetTargetCategory((BYTE)nCatTarget);
							}
						}
					}
				}
				break;
				
			case WM_MBUTTONDOWN:
			case WM_NCMBUTTONDOWN:
				if (MGCfg::Get().Option().GetMGAButton() == MG::BUTTON_MIDDLE)
				{
					AUTO_MUTEX(CMouseTracerManagerImpl5);
					
					if ((m_pTracer && !m_pTracer->IsTemporarySuspended()) || !m_pTracer)
					{
						if (m_pTracer && MGCfg::Get().Option().IsShowRecognitionOnStatusBar() && MGCfg::Get().Option().IsIdentifyTargetWindowClass())
							MGCfg::Get().Action().IdentifyTargetWindowClass(pMH->hwnd);
						
						if (!MGCfg::Get().Action().IsMGInAction())
						{
							if(-1 != (nCatTarget = MGCfg::Get().Action().CheckTargetValid(pMH->hwnd)))
							{
								if (m_pTracer && !CMouseTracerManagerImpl5::m_bTracerDeleted)
								{
									if (m_pTracer->GetHwnd() != pMH->hwnd)
									{
										if (::IsWindow(m_pTracer->GetHwnd()))
										{
											m_pTracer->Unhook();
											m_pTracer->Delete();
											m_pTracer = NULL;
										}
									}
									else 
										break;
								}
								m_pTracer = (MouseTracer *)new MouseTracer(TRUE, &CMouseTracerManagerImpl5::m_bTracerDeleted);
								m_pTracer->Hook(pMH->hwnd);
								m_pTracer->SetTargetCategory((BYTE)nCatTarget);
							}
						}
					}
				}
				break;
				
			case WM_RBUTTONDBLCLK:
			case WM_LBUTTONDBLCLK:
			case WM_MBUTTONDBLCLK:
				if ((nCatTarget = MGCfg::Get().Action().CheckTargetValid(pMH->hwnd)) != -1)
				{
					AUTO_MUTEX(CMouseTracerManagerImpl5);
					
					if ((m_pTracer && !m_pTracer->HasMouseCapture()) || !m_pTracer)
					{
						if (m_pTracer && !CMouseTracerManagerImpl5::m_bTracerDeleted)
						{
							if (m_pTracer->GetHwnd() != pMH->hwnd)
							{
								if (::IsWindow(m_pTracer->GetHwnd()))
								{
									m_pTracer->Unhook();
									m_pTracer->Delete();
									m_pTracer = NULL;
								}
							}
							else 
								break;
						}
						m_pTracer = (MouseTracer *)new MouseTracer(TRUE, &CMouseTracerManagerImpl5::m_bTracerDeleted);
						m_pTracer->Hook(pMH->hwnd);
						m_pTracer->SetTargetCategory((BYTE)nCatTarget);
					}
				}
				break;
				
			default:
				break;
			}
		}
    }
	
	return ::CallNextHookEx(pThis->GetHHOOK(), nCode, wParam, lParam);
}
